float sunRadius = 20; //"radius" of the sun
float earthRadius = 4; //"radius" of the earth

/* Body class
* creates a Body object which represents either a star or a planet
*/
class Body{
  public float radi;
  public color ink;
  public boolean isStar;
  public boolean isPlanet;
  public int element;
  
  /*constructor
  * sets the parameters of the body and what type of body it is
  */
  Body(float rr, color c, boolean is, boolean ip, int formula) {
    this.radi = rr;
    this.ink = c;
    this.isStar = is;
    this.isPlanet = ip;
    this.element = formula;
    
    if(isStar)
      this.radi = this.radi*sunRadius;
    if(isPlanet)
      this.radi = this.radi*earthRadius;
  }
  
  /*draws the body*/
  void display( float xpos, float ypos ){
    ellipseMode(CENTER);
    fill(this.ink);
    ellipse( xpos, ypos, this.radi, this.radi);
  }
}

/*PlanetType class
* creates an object describing a type of planet
*/
class PlanetType{
  public float planetMaximum;
  public float planetSizeMax;
  public float planetSizeMin;
  public int planetFormula;
  public color planetColor;
  
  /*constructor, pulls planet data*/
  PlanetType(float planetData[] ){
    this.planetMaximum = planetData[0];
    this.planetSizeMax = planetData[1];
    this.planetSizeMin = planetData[2];
    this.planetFormula = (int)planetData[3];
    this.planetColor = color(planetData[4],planetData[5],planetData[6]);
  }
}

/*StarType class
* creates an object describing a type of star
*/
class StarType {
   public float maxPlanets;
   public float starFormula;
   public float starSizeMax;
   public float starSizeMin;
   public int[] planetsT; 
   public color starColor;
   
   /*constructor, fills in the star data*/
   StarType(float[] starData, String pTypes){
     this.maxPlanets = starData[0];
     this.starSizeMax = starData[1];
     this.starSizeMin = starData[2];
     this.starFormula = starData[3];
     this.starColor = color(starData[4],starData[5],starData[6]);
     String[] inter = split(pTypes, ":");
     this.planetsT = int(inter);
   }
   
   int[] getPlanets()
   {
     return this.planetsT;
   }
}

/*SystemType class
* creates an object describing a type of solar system
*/
class SystemType {
  public int rank;
  public int numStars;
  public int maxObjects;
  public int[] starsT;
  
  /*constructor, pulls system data*/
  SystemType(int[] sysData, String sTypes){
    this.rank = sysData[0];
    this.numStars = sysData[1];
    this.maxObjects = sysData[2];
    this.starsT = int(split(sTypes, ":"));
  }
}

/*SolarSystem class
* creates an object that contains all the solar system data
*/
class SolarSystem {
  public int sysType;
  public int numStars;
  public int numPlanets;
  public String fullFormula;
  public Body[] stars;
  public Body[] planets;
  public boolean isValid;
  public boolean isBlue;
  public boolean hasHabitable;
  
  /*constructor, limits the system types by the difficulty rank*/
  SolarSystem(int rank){
    if( rank > 0)
      this.sysType = (int)random(0,rank);
    else
      this.sysType = 0;
      this.isValid = true;
  }
  
  /*creates a solar system randomly*/
  void genSystem(){
    this.isValid = true;
    this.numStars = SystemTypes[sysType].numStars;
    this.numPlanets = SystemTypes[sysType].maxObjects - numStars;
    this.stars = new Body[numStars];
    this.planets = new Body[numPlanets];
    /*check for the easy system type*/
    if(this.sysType < 1)
    {
      /*build the single star, randomly select type*/
      int starType = (int)random(0,5);
      this.stars[0] = new Body(random(StarTypes[starType].starSizeMin, StarTypes[starType].starSizeMax), StarTypes[starType].starColor, true, false, (int)StarTypes[starType].starFormula);
      this.fullFormula = ""+this.stars[0].element;
      /*generate the planets randomly*/
      for(int i = 0; i < this.numPlanets; i++)
      {
        int[] endplanet = StarTypes[starType].getPlanets();
        int planetType = (int)random(0,endplanet[endplanet.length-1]);
        while(planetType == 4 && hasHabitable)
          planetType = (int)random(0,endplanet[endplanet.length-1]);
        if(planetType == 4 && hasHabitable == false)
          this.hasHabitable = true;
        this.planets[i] = new Body(random(PlanetTypes[planetType].planetSizeMin, PlanetTypes[planetType].planetSizeMax), PlanetTypes[planetType].planetColor, false, true, PlanetTypes[planetType].planetFormula);
        this.fullFormula = this.fullFormula + ":" + this.planets[i].element;
      }
    }
  }
  
  /*build a solar system from an element list*/
  void buildSystem(String elements){
    int starcount = 0;
    int planetcount = 0;
    int hCount = 0;
    int[] recipe = int(split(elements, ":"));
    stars = new Body[recipe.length];
    planets = new Body[recipe.length];
    for(int j = 0; j < recipe.length;j++)
    {
        if(recipe[j] == 0 || recipe[j] == 2 || recipe[j] == 4 || recipe[j] == 7 || recipe[j] == 9)
        {
          stars[starcount] = new Body(random(Elements[recipe[j]][1],Elements[recipe[j]][0]), color(Elements[recipe[j]][2],Elements[recipe[j]][3],Elements[recipe[j]][4]),true,false,recipe[j]);
          starcount++;
          if(recipe[j] == 2)
            this.isBlue = true;
        }
        else
        {
          planets[planetcount] = new Body(random(Elements[recipe[j]][1],Elements[recipe[j]][0]), color(Elements[recipe[j]][2],Elements[recipe[j]][3],Elements[recipe[j]][4]),false,true,recipe[j]);
          planetcount++;
          if(recipe[j] > 2 && isBlue)
          {
              this.isValid = false;
              println("false by blue");
          }
          if(recipe[j] == 1)
          {
            hCount++;
            this.hasHabitable = true;
          }
        }
    }
    /*check for conditions that make the system invalid*/
    /*more than a trinary system*/
    if(starcount > 3)
      this.isValid = false;
    /*too many stars for a habitable planet*/
    if(starcount > 1 && hasHabitable)
      this.isValid = false;
    /*too many habitable planets*/
    if(hCount > 1)
      this.isValid = false;
    if(!isValid)
      println("not valid by #stars or #habitable");
      
    numStars = starcount;
    numPlanets = planetcount;
      
    if(isValid)
      this.displaySystem();
    else
      this.explode();
    
  }
  
  /*build the solar system on the screen*/
  void displaySystem(){
    float startx = -this.stars[0].radi;
    float starty = height/2;
    int x = 30;
    for(int k = 0; k < this.numStars; k++)
    {
      startx = startx + this.stars[k].radi;
      this.stars[k].display(startx, starty);
      println("startx = " + startx);
      startx = startx + this.stars[k].radi/4;
    }
    
    for(int k = 0; k < this.numPlanets; k++)
    {
      startx = startx + this.planets[k].radi;
      this.planets[k].display(startx, starty);
      println("startx = " + startx);
      startx = startx + this.planets[k].radi/2;
    }

    fill(155);
    text("Not to scale.", x, 700);    
  }
  
  /*create the explosion that indicates a failed system*/
  void explode() {
    background(0);
    stroke(255);
    strokeWeight(10);

    for( int i = 0; i < NUM ;i++) {
      point( exppos[i].x + width/2, exppos[i].y + height );
    }

    for( int i = 0; i < NUM; i++) {
      exppos[i].add(expvel[i]);
    }
  }
  
}

